import math
import copy  # Required for Deep Copy

# Topic: Lists, Mutability, List Comprehension, Copy vs Deepcopy

# ==========================================
#        HELPER FUNCTIONS (Math Logic)
# ==========================================
# These functions are provided for you.
def is_prime(n):
    """Checks if a number is prime."""
    if n < 2: return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0: return False
    return True

def is_perfect_square(n):
    """Checks if a number is a perfect square."""
    if n < 0: return False
    root = int(math.sqrt(n))
    return root * root == n


# ==========================================
#        LIST GENERATOR FUNCTIONS
# ==========================================

def get_odd_numbers(data_list):
    """
    Returns a new list containing only odd numbers from the input.
    MUST USE: List Comprehension.
    """
    # TODO 1: Implement List Comprehension
    # Syntax: [expression for item in list if condition]

    # WRITE YOUR CODE____start
    return [x for x in data_list if x%2 ==1] # Placeholder. Replace with your code.
    # WRITE YOUR CODE____end

def get_prime_numbers(data_list):
    """
    Returns a new list containing only prime numbers.
    MUST USE: List Comprehension and 'is_prime()' helper function.
    """
    # TODO 2: Implement List Comprehension

    # WRITE YOUR CODE____start
    return [i for i in data_list if is_prime(i)== True] # Placeholder. Replace with your code.
    # WRITE YOUR CODE____end

def get_square_numbers(data_list):
    """
    Returns a new list containing only perfect squares.
    MUST USE: List Comprehension and 'is_perfect_square()' helper function.
    """
    # TODO 3: Implement List Comprehension

    # WRITE YOUR CODE____start
    return [s for s in data_list if is_perfect_square(s) == True] # Placeholder. Replace with your code.
    # WRITE YOUR CODE____end


# ==========================================
#        DATA PREPARATION AREA
# ==========================================
print("--- DATA PREPARATION ---")

# Tuple (Immutable)
MyNumbers_tuple = (1, 2, 3, 4, 7, 9, 11, 16, 17, 19, 25, 30, 36)

# TODO 4: Tuple to List Conversion
# 1. Create a list named "MyNumbers_list" by converting "MyNumbers_tuple".
# 2. Print the elements of "MyNumbers_list".

# WRITE YOUR CODE____start
MyNumbers_list = [t for t in MyNumbers_tuple] # Placeholder
print("Maim list:",MyNumbers_list)
# WRITE YOUR CODE____end


# TODO 5: Create Sub-Lists using Functions
# Call the functions you wrote in TODO 1, 2, and 3 using 'MyNumbers_list' as input.
# Store the results in variables: ListA, ListB, and ListC.

# WRITE YOUR CODE____start
ListA = [i for i in get_odd_numbers(MyNumbers_list)] # Placeholder
ListB = [i for i in get_prime_numbers(MyNumbers_list)] # Placeholder
ListC = [i for i in get_square_numbers(MyNumbers_list)] # Placeholder
# WRITE YOUR CODE____end


print(f"ListA (Odd):      {ListA}")
print(f"ListB (Prime):    {ListB}")
print(f"ListC (Square):   {ListC}")


# ==========================================
#        NESTED LISTS & ALIASING
# ==========================================
print("\n--- SCENARIO 1: Creating a 2D Matrix (Aliasing) ---")

# Creating a 2D list (List of Lists)
# NOTE: 'two_d_form' holds REFERENCES to ListA, ListB, and ListC.
# It does NOT contain independent copies.
two_d_form = [ListA, ListB, ListC]

print(f"2D Matrix (Initial): {two_d_form}")
print("-" * 30)


# ==========================================
#        SCENARIO A: SAFE FILTERING
# ==========================================

def keep_small_numbers_safe(data_2d, threshold):
    """
    1. Creates a DEEP COPY of the input matrix.
    2. Removes numbers larger than 'threshold' from the COPY.
    3. Returns the safe copy (Original data remains untouched).
    """
    print(f"\n[Processing] Removing numbers > {threshold} (Using Deep Copy)...")

    # TODO 6: Deep Copy & Safe Removal
    # Step A: Create a '=' 'safe_copy' using copy.deepcopy(data_2d).
    #         (If we use, we create an Alias. If we use .copy(), we create a Shallow Copy.
    #          Since this is a Nested List, we MUST use Deep Copy).

    # Step B: Iterate through 'safe_copy' to find sublists.

    # Step C: Iterate through numbers in the sublist.
    #         CRITICAL: You cannot remove items from a list while iterating over it directly.
    #         You must iterate over a SLICE (copy) of the sublist (e.g., sublist[:]).

    # Step D: If number > threshold, remove it from the sublist.
    safe_copy = copy.deepcopy(data_2d)
    for sublist in safe_copy:
        for t in sublist[:]:
            if t > threshold:
                sublist.remove(t)
        '''this part is thinking
        copysublist = sublist[:]
        for h in copysublist:
            for t in h:
                newcopy = t[:]
                print("ooooooooo",newcopy)


        print("HHHHHHHHHHHHHHHH",copysublist)
'''

    # WRITE YOUR CODE____start
    return safe_copy  # Placeholder. Replace with your code.
    # WRITE YOUR CODE____end


# Calling the safe function
matrix_small = keep_small_numbers_safe(two_d_form, 15)

print(f"New Matrix (Small Only): {matrix_small}")


# ==========================================
#        VERIFICATION STEP
# ==========================================
print("\n--- CHECK: Is Original Data Corrupted? ---")
print(f"Original Matrix: {two_d_form}")

# Check if '25' still exists in the original ListA (Index 0 of two_d_form)
# 25 is > 15, so it should be gone in 'matrix_small' but present in 'two_d_form'.
if len(two_d_form) > 0 and len(two_d_form[0]) > 0 and 25 in two_d_form[0]:
    print(">> SUCCESS: Original data is safe! (Thanks to Deep Copy)")
else:
    # Since the list is empty at the start, this will trigger until student writes code.
    print(">> FAIL: Original data missing or corrupted! (Did you implement the functions?)")


# ==========================================
#        SCENARIO B: REUSING ORIGINAL DATA
# ==========================================
print("\n" + "="*40)
print("--- SCENARIO 2: Filtering for Large Numbers ---")

def keep_large_numbers_safe(data_2d, threshold):
    """
    Creates a deep copy and removes numbers smaller than or equal to threshold.
    Returns the new matrix with only LARGE numbers.
    """
    print(f"[Processing] Removing numbers <= {threshold}...")

    # TODO 7: Implement Deep Copy & Inverse Logic
    # 1. Create deep copy.
    # 2. Iterate (using slice for safety).
    # 3. If number <= threshold, remove it.

    # WRITE YOUR CODE____start
    safe_copy = copy.deepcopy(data_2d)
    for sublist2 in safe_copy:
        for y in sublist2[:]:
            if y <= threshold:
                sublist2.remove(y)

    return safe_copy # Placeholder. Replace with your code.
    # WRITE YOUR CODE____end

# Using the original 'two_d_form' again (since it is not broken)
matrix_large = keep_large_numbers_safe(two_d_form, 15)

print(f"New Matrix (Large Only): {matrix_large}")

print("\n------------- FINAL SUMMARY -------------")
print(f"Original Source: {MyNumbers_list}")
print(f"ListA (Original): {ListA}")
print(f"ListB (Original): {ListB}")
print(f"ListC (Original): {ListC}")
print(f"2D Form (Ref):    {two_d_form}")
print(f"Small Nums Mat:   {matrix_small}")
print(f"Large Nums Mat:   {matrix_large}")
